
import { Path, Vector2, Vector3 } from "three";

import CommonUtils from "./CommonUtils";
import AModelFmulaUtils from "./AModelFmulaUtils";
import { BIM } from "./BIM";
import ConstDef from "./ConstDef";
import Vector2M from "./Vector2M";
import VectorUtils from "./VectorUtils";
import { AK } from "./enum";

/**
 * 异形平板
 */
export default class IrregularPlateUtil {

    /**
    * 判断点是否在多边形内部
    * @param {Point} point 点坐标
    * @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
    * @returns
    */
    static isInPolygon(point: Vector2M, points: Vector2M[]) {
        if (!Array.isArray(points) || points.length < 3) {
            console.error("多边形坐标集合不能少于3个");
            return;
        }
        const n = points.length;
        let nCross = 0;
        for (let i = 0; i < n; i++) {
            const p1 = points[i];
            const p2 = points[(i + 1) % n];
            // 求解 y=p.y 与 p1 p2 的交点
            // p1p2 与 y=p0.y平行
            if (p1.y === p2.y) continue;
            // 交点在p1p2延长线上
            if (point.y < Math.min(p1.y, p2.y)) continue;
            // 交点在p1p2延长线上
            if (point.y >= Math.max(p1.y, p2.y)) continue;
            // 求交点的 X 坐标
            const x = ((point.y - p1.y) * (p2.x - p1.x)) / (p2.y - p1.y) + p1.x;
            // 只统计单边交点
            if (x > point.x) nCross++;
        }
        return nCross % 2 === 1;
    }

    /**
    * 获取多边形重心（质心）
    * @param {Point[]} points 点坐标数组 [{x:0,y:0}...]
    */
    static getPolygonBaryCenter(points: Vector2M[]) {
        if (!Array.isArray(points) || points.length < 3) {
            console.error("多边形坐标集合不能少于3个");
            return;
        }
        const result = new Vector2M(0, 0);
        let area = 0;
        for (let i = 1; i <= points.length; i++) {
            const curX = points[i % points.length].x;
            const curY = points[i % points.length].y;
            const nextX = points[i - 1].x;
            const nextY = points[i - 1].y;
            const temp = (curX * nextY - curY * nextX) / 2;
            area += temp;
            result.x += (temp * (curX + nextX)) / 3;
            result.y += (temp * (curY + nextY)) / 3;
        }
        result.x /= area;
        result.y /= area;
        return result;
    }

    /**
     * 判断坐标数组是否顺时针（默认为false）
     * @param points 点坐标数组 [{x:0,y:0}...]
     * @returns 
     */
    static isClockwise(points: Vector2M[] | Vector2[]) {
        // 三个点可以判断矢量是顺时针旋转还是逆时针旋转的，但由于可能存在凹边，所以并不是任意三点都可以正确反映多边形的走向
        // 因此需要取多边形中绝对是凸边的点来判断，
        // 多边形中的极值点（x最大或x最小或y最大或y最小）它与相邻两点构成的边必然是凸边，因此我们先取出多边形中的极值点，再由极值点和其前后两点去判断矢量的走向，从而判断出多边形的走向。
        if (!Array.isArray(points) || points.length < 3) {
            console.error("多边形坐标集合不能少于3个");
            return false;
        }
        let coords = JSON.parse(JSON.stringify(points));

        if (coords[0] === coords[coords.length - 1]) {
            coords = coords.slice(0, coords.length - 1);
        }
        coords = coords.reverse();
        let maxXIndex = 0;
        let maxX = parseFloat(coords[maxXIndex].x);
        let c1;
        let c2;
        let c3;
        for (let i = 0; i < coords.length; i++) {
            if (parseFloat(coords[i].x) > maxX) {
                maxX = parseFloat(coords[i].x);
                maxXIndex = i;
            }
        }
        if (maxXIndex === 0) {
            c1 = coords[coords.length - 1];
            c2 = coords[maxXIndex];
            c3 = coords[maxXIndex + 1];
        } else if (maxXIndex === coords.length - 1) {
            c1 = coords[maxXIndex - 1];
            c2 = coords[maxXIndex];
            c3 = coords[0];
        } else {
            c1 = coords[maxXIndex - 1];
            c2 = coords[maxXIndex];
            c3 = coords[maxXIndex + 1];
        }
        const x1 = parseFloat(c1.x);
        const y1 = parseFloat(c1.y);
        const x2 = parseFloat(c2.x);
        const y2 = parseFloat(c2.y);
        const x3 = parseFloat(c3.x);
        const y3 = parseFloat(c3.y);
        const s = (x1 - x3) * (y2 - y3) - (x2 - x3) * (y1 - y3);
        return s < 0;
    }

    static scalePolygon(points: Vector2M[], extra: number) {
        if (!Array.isArray(points) || points.length < 3) {
            console.error("多边形坐标集合不能少于3个");
            return;
        }
        const ps = points;
        // 通过顺时针判断取正值还是负值
        const extra0 = this.isClockwise(ps) ? -extra : extra;

        const len = ps.length;
        const polygon = [];
        for (let i = 0; i < len; i++) {
            const point = ps[i];
            const point1 = ps[i === 0 ? len - 1 : i - 1];
            const point2 = ps[i === len - 1 ? 0 : i + 1];

            // 向量PP1

            let pp1 = point1.clone().sub(point).normalize();
            let pp2 = point2.clone().sub(point).normalize();

            // PQ距离
            const vectorLen = -extra0 / Math.sqrt((1 - (pp1.x * pp2.x + pp1.y * pp2.y)) / 2);

            // 根据向量的叉乘积来判断角是凹角还是凸角
            if (pp1.x * pp2.y + -1 * pp1.y * pp2.x < 0) {
                pp1.negate();
                pp2.negate();
            }
            // PQ的方向
            let pq: Vector2M;
            let addp: Vector2M;
            // 平行
            if (Math.abs(pp1.x * pp2.y + -1 * pp1.y * pp2.x) < 0.01) {
                let cp = this.getPolygonBaryCenter(points);
                let cv1 = new Vector2M(-pp1.y, pp1.x).normalize();
                let cv2 = new Vector2M(pp1.y, -pp1.x).normalize();
                let vv = cp.clone().sub(point).normalize();
                let angle1 = vv.angleTo(cv1);

                if (angle1 < Math.PI / 2) {
                    addp = cv1.clone().setLength(-extra)
                }
                else {
                    addp = cv2.clone().setLength(-extra)
                }

            }
            else {
                pq = pp1.clone().add(pp2);
                const n = vectorLen / pq.length();
                addp = pq.multiplyScalar(n)
            }




            polygon[i] = point.clone().add(addp);
        }

        return polygon;
    }


    static getLineFaceData(count: number, vertices: number[], indices: number[], facevec: Vector3[][],
        face: Vector3[], holes: any[], start: Vector2M, end: Vector2M, bkh: number, kongpoint: any[], data: any) {
        let len = end.distanceTo(start);
        let path = [
            new Vector2M(0, 0),
            new Vector2M(0, bkh),
            new Vector2M(len, bkh),
            new Vector2M(len, 0)
        ]

        let dir = end.clone().sub(start).normalize();
        let angle = dir.clone().angle();
        let cosA = Math.cos(angle);
        let sinA = Math.sin(angle);

        count = CommonUtils.getFaceByFunction(count, vertices, indices, facevec, face, path, holes, null,
            (x, y) => [start.x + x * cosA, start.y + x * sinA, y]);

        CommonUtils.dealHole(kongpoint, (a: number, b: number, c: number) => {

            return [start.x + a * cosA, start.y + a * sinA, b];
        });
        data.kongpoint.push(...kongpoint);


        return count;
    }

    static getIrregularPlateVI(data: ALParams, islptxPlate: boolean = true, err: number = 0.1): any[] {

        // 数据
        let vertices: any[] = [];
        let indices: any[] = [];
        let facevec: any[] = [];
        // 计数
        let count: number = 0;

        // 形状
        let path: Vector2M[] = [];
        let holes: Path[] = [];
        let holePath: Path;

        let L: number = +data.L;
        let W: number = +data.W;
        let GMT: number = +data.GMT;
        let HLW: number = +data.HLW;
        let HLH: number = +data.HLH;
        let BKH: number = +data.BKH;
        let bkT: number = +data.bkT;
        let BKDia: number = +data.BKDia;
        let BKKJMDis: number = +data.BKKJMDis

        // 移动端点
        data.duan = [];

        data.kongpoint = [];
        let kongpoint = [];

        holes = [];

        let duanAry = [];
        let nduanAry = [];
        // 光面
        let i: number;
        let gmtPath: Vector2M[] = []
        let cutplateoutline = data.cutplateoutline;
        let oxAry: number[] = [];
        let oyAry: number[] = [];
        for (i = 0; i < cutplateoutline.length; i += 2) {
            gmtPath.push(new Vector2M(cutplateoutline[i], cutplateoutline[i + 1]));
            data.duan.push(cutplateoutline[i], cutplateoutline[i + 1], 0);
            duanAry.push(new Vector3(cutplateoutline[i], cutplateoutline[i + 1], 0));

            oxAry.push(cutplateoutline[i])
            oyAry.push(cutplateoutline[i + 1])
        }

        data["p_ox"] = Math.max(...oxAry) - Math.min(...oxAry);
        data["p_oy"] = Math.max(...oyAry) - Math.min(...oyAry);

        if (duanAry.length > 0) duanAry.push(duanAry[0]);

        let scalePath = this.scalePolygon(gmtPath, -bkT)

        for (i = 0; i < scalePath.length; i++) {
            nduanAry.push(new Vector3(scalePath[i].x, scalePath[i].y, 0));
        }

        if (nduanAry.length > 0) nduanAry.push(nduanAry[0]);


        data.p_r1 = CommonUtils.checkRadiusIsLegal(data.p_r1);
        data.p_r2 = CommonUtils.checkRadiusIsLegal(data.p_r2);
        data.p_r3 = CommonUtils.checkRadiusIsLegal(data.p_r3);

        data.p_r1dw = CommonUtils.fliterOuterPoint(data.p_r1dw, gmtPath, false, false, false, true, data.p_r1 + "");
        data.p_r2dw = CommonUtils.fliterOuterPoint(data.p_r2dw, gmtPath, false, false, false, true, data.p_r2 + "");
        data.p_r3dw = CommonUtils.fliterOuterPoint(data.p_r3dw, gmtPath, false, false, false, true, data.p_r3 + "");


        CommonUtils.getGmtHoles(data.p_r1, data.p_r1dw, holes);
        CommonUtils.getGmtHoles(data.p_r2, data.p_r2dw, holes);
        CommonUtils.getGmtHoles(data.p_r3, data.p_r3dw, holes);

        CommonUtils.getGMTHolesToKongPoint(holes, data.kongpoint);

        let gmface: Vector3[];
        if (BIM.simpleMold) {
            gmface = [];
            for (i = 0; i < gmtPath.length - 1; i++) {
                gmface.push(new Vector3(gmtPath[i].x, gmtPath[i].y, 0));
            }

            count = CommonUtils.getFaceByFunction(count, vertices, indices, facevec, gmface, gmtPath, holes, AK.p_r1dw,
                (a: number, b: number) => [a, b, 0]);
        }
        else {
            count = CommonUtils.getShapeVI(count, vertices, indices, facevec, gmtPath, holes, 0, GMT, 0, true, "p_r1dw");
        }



        // 边框
        let znormal = ConstDef.znormal;

        let one: Vector3;
        let two: Vector3;
        let three: Vector3;
        let four: Vector3;

        let bone: Vector3;
        let btwo: Vector3;
        let bthree: Vector3;
        let bfour: Vector3;

        let topface: Vector3[];
        let bottomface: Vector3[];

        let tpath = [...gmtPath, gmtPath[0]];
        let bpath = [...scalePath, scalePath[0]];

        let type: number = 0;
        let bkh: number = BKH ?? 65;
        let len: number = 0;

        let xv: number, yv: number;

        let p_bkkw: string = "";
        let bkDatas = data["bkDatas"];

        for (i = 0; i < duanAry.length - 1; i++) {

            // 边框类型
            type = bkDatas[i].p_bktype;
            if (type != 0) continue;

            // 顶
            bkh = +bkDatas[i].BKH;

            one = duanAry[i];
            two = duanAry[i + 1];
            three = two.clone().add(znormal.clone().setLength(bkh));
            four = one.clone().add(znormal.clone().setLength(bkh));


            len = two.distanceTo(one);

            // 孔位
            p_bkkw = bkDatas[i].p_bkkw;
            let bkkpos = AModelFmulaUtils.getHolesbyExpress(p_bkkw, len, 2, data, 'p_bkkw');
            // bkDatas[i].p_bkkw = data["p_bkkw"];
            bkDatas[i].p_bkkw = data["bkDatas"][i]["p_bkkw"];
            holes = [];

            kongpoint = [];
            for (let b = 0; b < bkkpos.length; b++) {
                xv = bkkpos[b];
                yv = bkh - BKKJMDis;

                if (xv < BKDia / 2 || xv > len - BKDia / 2) {
                    continue;
                }

                holePath = new Path();
                holePath.moveTo(xv, yv)
                    .absarc(xv, yv, BKDia / 2, 0, Math.PI * 2, true);

                holes.push(holePath);

                kongpoint.push(xv, yv, 0);
            }

            topface = [one, two, three, four];
            count = this.getLineFaceData(count, vertices, indices, facevec, topface, holes, tpath[i], tpath[i + 1], bkh, kongpoint, data);

            if (!BIM.simpleMold) {
                // 底
                bone = nduanAry[i];
                btwo = nduanAry[i + 1];

                bthree = btwo.clone().add(znormal.clone().setLength(bkh));
                bfour = bone.clone().add(znormal.clone().setLength(bkh));

                bottomface = [bone, btwo, bthree, bfour];

                let side = CommonUtils.getSideVIF([...bottomface, bottomface[0]], [...topface, topface[0]], count);
                vertices.push(...side[0]);
                indices.push(...side[1]);
                facevec.push(...side[2]);
                count = side[3];

                let dir = two.clone().sub(one);
                let pp = VectorUtils.getProjectVector(one, two, bone);
                let dis = pp.distanceTo(one);

                let tdir = pp.clone().sub(one);

                let isSameDir = VectorUtils.isDirectionSame(dir, tdir);

                if (!isSameDir) dis = -dis;

                holes = [];
                kongpoint = [];
                for (let b = 0; b < bkkpos.length; b++) {
                    xv = bkkpos[b] - dis;
                    yv = bkh - BKKJMDis;

                    if (xv < BKDia / 2 || xv > len - BKDia / 2) {
                        continue;
                    }

                    holePath = new Path();
                    holePath.moveTo(xv, yv)
                        .absarc(xv, yv, BKDia / 2, 0, Math.PI * 2, true);

                    holes.push(holePath);

                    kongpoint.push(xv, yv, 0);
                }

                count = this.getLineFaceData(count, vertices, indices, facevec, bottomface, holes, bpath[i], bpath[i + 1], bkh, kongpoint, data);
            }
        }

        // 横肋
        if (data.p_hldw && data.p_hldw !== "") {

            data.p_hldw = CommonUtils.fliterOuterPoint(data.p_hldw, gmtPath, false, false, false);

            let p_hldw = data.p_hldw;
            let hlpos = AModelFmulaUtils.getHolesbyExpress(p_hldw, 0, 3);

            if (hlpos.length > 1) {
                let shlpos: Vector3 = new Vector3(hlpos[0].x, hlpos[0].y, 0);
                let ehlpos: Vector3 = new Vector3(hlpos[1].x, hlpos[1].y, 0);

                let hldir = ehlpos.clone().sub(shlpos);

                let wdir = hldir.clone().cross(ConstDef.znormal);

                let woffset = wdir.clone().setLength(HLW / 2);
                let bhlp1 = shlpos.clone().add(woffset);
                let bhlp2 = shlpos.clone().sub(woffset);
                let bhlp3 = ehlpos.clone().sub(woffset);
                let bhlp4 = ehlpos.clone().add(woffset);

                let hlpath = [
                    new Vector2(bhlp1.x, bhlp1.y),
                    new Vector2(bhlp2.x, bhlp2.y),
                    new Vector2(bhlp3.x, bhlp3.y),
                    new Vector2(bhlp4.x, bhlp4.y)
                ]

                count = CommonUtils.getShapeVI(count, vertices, indices, facevec, hlpath, null, 0, 0, HLH, false, null);
            }
            else {
                data.p_hldw = "";
            }


        }

        return [vertices, indices, facevec];
    }

}